#include "PermutationEncodedSolution.h"

PermutationEncodedSolution::PermutationEncodedSolution()
{
}

PermutationEncodedSolution::PermutationEncodedSolution(vector<int> chromosome)
{
    this->chromosome = chromosome;
}

vector<int> PermutationEncodedSolution::getSolution()
{
    return chromosome;
}

void PermutationEncodedSolution::setSolution(vector<int> newChromosome) 
{
    chromosome = newChromosome;
}
 
void PermutationEncodedSolution::mutate(void)
{
	float mutationRate = Solution::getMutationRate() * 10000.0;
	double comparator = MyRandom::randDbl() * 10000;
	unsigned int mySize = chromosome.size();
	
	if (comparator < mutationRate)
	{
//		int swap1 = rand() % mySize;
//		int swap2 = rand() % mySize;
		
		int swap1 = MyRandom::randInt(0, mySize - 1);
		int swap2 = MyRandom::randInt(0, mySize - 1);

		// Make sure we don't swap a value with itself
		while (swap1 == swap2)
			swap2 = rand() % mySize;
	
		int temp = chromosome[swap1];
		chromosome[swap1] = chromosome[swap2];
		chromosome[swap2] = temp;
	}
}

void PermutationEncodedSolution::fillWithRandomPermutation(int min, int max) {
    if (min > max)
    {
    	int temp = min;
    	min = max;
    	max = temp;
    }
    
    for (int i = min; i <= max; ++i)
        chromosome.push_back(i);
         
    randomizeContentOrder();  
}

void PermutationEncodedSolution::randomizeContentOrder() {
    vector<int> bin;
    
    //move the contents of the solution into a temporary container
    while (chromosome.size() > 0) {
        bin.push_back(chromosome.back());
        chromosome.pop_back();
    }
    
    //then put them back in a random order
    while (bin.size() > 0) 
    {
        int pos = MyRandom::randInt(0, bin.size() - 1);
        vector<int>::iterator iter = bin.begin();
        iter += pos;
        
        chromosome.push_back(bin[pos]);
        bin.erase(iter);
    }    
}

Solution* PermutationEncodedSolution::clone() 
{
    PermutationEncodedSolution* returnMe = new PermutationEncodedSolution(chromosome);
    returnMe->setFitness(fitness);
    returnMe->setMutationRate(mutationRate);
    return returnMe;
}

ostream& PermutationEncodedSolution::outputSolution(ostream &output) 
{
    unsigned int size = chromosome.size();
    for (unsigned int i = 0; i < size; ++i)
        output << chromosome[i] << ' ';
    //TODO: Why are we displaying the fitness this way?  Is this some
    //environment specific formatting thing?  If so, it belongs there, 
    //not here.
    output << "(" << (100.0 / fitness) << ")";
    return output;
}

void PermutationEncodedSolution::addElement(int element)
{
	if (! hasElement(element))
		chromosome.push_back(element);
	else
		throw Invalid_element_exception();
}

void PermutationEncodedSolution::removeElement(int element)
{
	vector<int>::iterator i = findElement(element);
	if (*i == element)
		chromosome.erase(i);
	else
		throw Invalid_element_exception();
}

bool PermutationEncodedSolution::hasElement(int element)
{
	for (unsigned int i = 0; i < chromosome.size(); ++i)
	{
		if (chromosome[i] == element)
			return true;
	}
	return false;
}

SolutionType PermutationEncodedSolution::getSerializationIdentifyer() {
    return SF_PERMUTATION_ENCODED_SOLUTION;
}

/**
 * Searches for the specified element and returns an iterator pointing to that element,
 * if the element is not found it will point to chromosome.end(), thus a check must 
 * be made to differentiate between an element not found, or the element being found
 * at the end of the vector.
 * 
 */
vector<int>::iterator PermutationEncodedSolution::findElement(int element)
{
	vector<int>::iterator i = chromosome.begin();
	while (*i != element && i != chromosome.end())
		++i;
	
	return i;
}

const int& PermutationEncodedSolution::operator[](int index) const 
{ 
    return chromosome[index]; 
}

int& PermutationEncodedSolution::operator[](int index) 
{ 
    return chromosome[index]; 
}

unsigned int PermutationEncodedSolution::size() const 
{ 
    return chromosome.size(); 
}

int PermutationEncodedSolution::getElement(int index) const 
{ 
    return chromosome[index]; 
}

void PermutationEncodedSolution::setElement(int index, int value) 
{ 
    chromosome[index] = value; 
}
